package net

import (
	"errors"
	"gitee.com/CloudGuan/rpc-go-backend/idlrpc/common"
	"net"
	"strconv"
	"strings"
	"sync"
)

const (
	CONNECTED = iota
	RECONNECTED
	CLOSED
)

type netJob func()

var (
	listenermgr map[string]*listener //监听连接信息
	connlist    map[uint64]*connInfo //连接信息
	jobQueue    chan netJob          //网络消息队列
	stop        chan struct{}        //关闭信号
	once        sync.Once            //初始化
)

func init() {
	once.Do(func() {
		listenermgr = make(map[string]*listener)
		connlist = make(map[uint64]*connInfo)
		jobQueue = make(chan netJob, 128)
		stop = make(chan struct{}, 1)
	})
}

//idlrpc 网络层代码
func AddListener(proto, address string, handle AcceptHandle) error {
	switch proto {
	case "tcp":
	case "udp":
	default:
		return errors.New("proto type error !!!")
	}

	//check address
	index := strings.Index(address, ":")
	if index == -1 || index == len(address)-1 {
		return errors.New("address must be ip:port!!!")
	}

	if _, ok := listenermgr[address]; ok {
		return errors.New("重复监听！！！")
	}

	l, err := initListener(proto, address, handle)
	if err != nil {
		return err
	}

	listenermgr[address] = l
	return nil
}

func ConnectTo(proto, address string, handle ConnHandle) error {
	index := strings.Index(address, ":")
	if index == -1 || index == len(address)-1 {
		return errors.New("address must be ip:port!!!")
	}

	remoteip := address[:index]
	port, _ := strconv.Atoi(address[index+1:])

	remoteaddr := net.TCPAddr{}
	remoteaddr.IP = net.ParseIP(remoteip)
	remoteaddr.Port = port

	connptr := &connInfo{
		connId:    common.GetConnID(),
		status:    CLOSED,
		sendQueue: make(chan []byte, 16),
	}

	var err error
	connptr.conn, err = net.DialTCP(proto, nil, &remoteaddr)
	if err != nil {
		return err
	}
	connptr.ch = handle
	connptr.ch.OnConnect(connptr)
	connptr.startLoop()
	return nil
}

func CloseConn(connId uint64) error {
	if v, ok := connlist[connId]; ok {
		v.Close()
		return nil
	}
	return errors.New("Not Found ConnId")
}

func Start() {
	var err error
	for _, v := range listenermgr {
		err = nil
		err = v.startListen()
		if err != nil {
			//TODO add log
			continue
		}
		go v.startLoop()
	}
	go doTick()
}

func Close() {
	for _, v := range connlist {
		v.Close()
	}

	for _, v := range listenermgr {
		v.close()
	}

	stop <- struct{}{}
	<-stop
}

func ShutDown() {

}

func doTick() {
	var job netJob
	var more bool

	for {
		select {
		case job, more = <-jobQueue:
			if more == false {
				// channel is closed
				return
			}
			job()
		case <-stop:
			stop <- struct{}{}
			return
		}
	}
}
